﻿// Collection.cpp : 实现文件
//

#include "stdafx.h"
#include "GHCY.h"
#include "Ground.h"
//#include "../Hui/Hui.h"
#include "NodeColl.h"
#include "Floors.h"
#include "LogList.h"
#include "CollStarts.h"
#include "Collection.h"
#include "Collections.h"

// Collection

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif

/// <作者>Mr. Hu</作者>
/// <日期>202001009</日期>
/// <功能>同类端点集合类</功能>
/// <参数 name = ""></参数>
/// <返回></返回>
/// <说明>同类端点集合，用于神经网络计算</说明>
Collections::Collections()
{
	m_arrColls.SetSize(0, 10);
}

Collections::~Collections()
{
	for (size_t i = 0, length = m_arrColls.GetCount(); i < length; i++)
	{
		delete m_arrColls[i];
	}
}

/// <作者>Mr. Hu</作者>
/// <日期>202001009</日期>
/// <功能>添加一个集合</功能>
/// <参数 name = "pep">指向端点</参数>
/// <返回></返回>
/// <说明>端点总数</说明>
INT_PTR Collections::add(Collection* pColl)
{
	return m_arrColls.Add(pColl);
}

/// <作者>Mr. Hu</作者>
/// <日期>202001009</日期>
/// <功能>取端点数量</功能>
/// <参数 name = ""></参数>
/// <返回>端点总数</返回>
/// <说明></说明>
INT_PTR Collections::getSize()
{
	return m_arrColls.GetCount();
}

/// <作者>Mr. Hu</作者>
/// <日期>20201009</日期>
/// <功能>初始化神经元</功能>
/// <参数 name = ""></参数>
/// <返回></返回>
/// <说明></说明>
void Collections::initNeural()
{
	m_arrColls.SetSize(0, 8);
	for (size_t i = 0, length = theApp.m_pGround->m_arrEndPoint.GetCount(); i < length; i++)
	{
		EndPoint* pep = theApp.m_pGround->m_arrEndPoint[i];
		Collection* pCollection = 0;

		// bufCode 不同，新加一个集合
		// 只适用数据库中所有相同的bufCode排在一起的情况，不适用原江西重庆道路
		// 20210327 改成bufCode排在一起也能用
		for (size_t j = 0, nColls = m_arrColls.GetCount(); j < nColls; j++)
		{
			Collection* pColl = m_arrColls[j];
			if (*(UINT64*)pep->m_bufCode == *(UINT64*)pColl->m_bufCode)		// 遍历相同的编号
			{
				pCollection = pColl;
				break;
			}
		}

		if (!pCollection)
		{
			if(pep->m_bStart)
				pCollection = new CollStarts();
			else
				pCollection = new Collection();
			//switch (pep->m_pointType)
			//{
			//case POINT_TYPE::POINT_START:		// 发车点
			//	pCollection = new CollStarts();
			//	break;
			//default:
			//	pCollection = new Collection();
			//	break;
			//}
			m_arrColls.Add(pCollection);
			*(UINT64*)pCollection->m_bufCode = *(UINT64*)pep->m_bufCode;	// 编号
			pCollection->m_pointType.m_nType = pep->m_nType;	// 类型
			// pCollection->m_nSerial = i + 61; 暂时不用了

			// 初始化优先级
			if (pep->isRecharge()) // 充电
				pCollection->m_priority0.p1 = 10;// pep->m_bufCode[1] == L'3' ? 10 : 0;// 11 : 0;	// 在线充电是10，离线或发车充电是0	// 20210516 都改为0，保证坚美雅洁存货后不动	// 20210713 改回10，否则不充电
			else if (pep->isProduct())	// ???? 20201213 产品优先级高二级
				pCollection->m_priority0.p1 = pep->m_bufCode[3];					// 最后一位表示优先级
			else if (pep->isStore())	// ???? 20201213 空托区高一级
				pCollection->m_priority0.p1 = pep->m_bufCode[3];					// 最后一位表示优先级
			else if (pep->isMiddle())	// 中间等待点
				pCollection->m_priority0.p1 = 10;
			else if(pep->isStart())
				pCollection->m_priority0.p1 = 0;
			else
				pCollection->m_priority0.p1 = 0;
		}
		ASSERT(pCollection);
		pCollection->add(pep);
		pep->m_pColl = pCollection;
	}

	// 各集合的关系
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection& colli = *m_arrColls[i];
		ASSERT(colli.m_arrPoint.GetCount());
		EndPoint* pep = colli.m_arrPoint[0];
		for (size_t j = 0; j < nColls; j++)
		{
			Collection& collj = *m_arrColls[j];
			if (pep->isParent(collj.m_arrPoint[0]))		// ???? 可能是多对多的关系，只能保留一个？
			{
				colli.m_pPreColl = &collj;		// 设置对应关系
				collj.m_pNextColl = &colli;
			}
		}
	}

	// 20220705 初始化节点表，卡号相同的点合到一起
	m_nodesColl.init();

}

/// <作者>Mr. Hu</作者>
/// <日期>20201101</日期>
/// <功能>从集合中判断指定点是不是充电点</功能>
/// <参数 name = "prn">RoadNode * 指定的道路节点</参数>
/// <返回>TRUE 是充电点，</返回>
/// <说明></说明>
EndPoint* Collections::isRecharge(RoadNode* prn)
{
	size_t length = m_arrColls.GetCount();
	for (size_t i = 0; i < length; i++)	// 遍历集合
	{
		Collection& coll = *m_arrColls[i];
		if (!((EndPoint::PointType&)coll.m_pointType).m_bRecharge)		// 不是充电集
		//if (coll.m_pointType != POINT_TYPE::POINT_RECHARGE)			// 不是充电集
			continue;					// 跳过

		EndPoint* pep = coll.sameCard(prn);
		if (pep)			// 卡号相同
			return pep;
	}
	return 0;
}

/// <作者>Mr. Hu</作者>
/// <日期>20240521</日期>
/// <功能>获取充电点集合</功能>
/// <参数 name = ""></参数>
/// <返回>0 无充电点，否则返回充点点集合</返回>
/// <说明></说明>
Collection* Collections::getRecharges()
{
	size_t length = m_arrColls.GetCount();
	for (size_t i = 0; i < length; i++)	// 遍历集合
	{
		Collection& coll = *m_arrColls[i];
		//if (coll.m_pointType == POINT_TYPE::POINT_RECHARGE)			// 不是充电集
		if(coll.m_pointType.m_bRecharge)
			return &coll;					// 跳过
	}
	return 0;
}

/// <作者>Mr. Hu</作者>
/// <日期>20210412</日期>
/// <功能>计算集合优先级</功能>
/// <参数 name = ""></参数>
/// <返回>1 成功分配任务，否则 0</返回>
/// <说明>计算集合优先级，并分配AGV</说明>
bool Collections::updatePriority( Car* pCar )
{
	// 优先回到AGV的起始点取货
	//if (pCar->m_arrPath.GetCount())		// 只用一次，不用锁定
	//{
	//	PathNode& pn = pCar->m_arrPath[0];
	//	//if (pn.m_nPointID)
	//	//{
	//	//	EndPoint* pep = theApp.m_pGround->m_arrEndPoint[pn.m_nPointID - 1];
	//	//	if (pep->isProduct()
	//	//		&& pep->allowPickup(pCar)
	//	//		)
	//	//	{
	//	//		int nGet = pep->getRoadNode()->m_nID - 1;	// 如果是同类，直接导到终点
	//	//		if (theApp.m_pGround->carStartGet(pCar, nGet))
	//	//		{
	//	//			// 设置AGV对应的呼叫点通讯实例
	//	//			//pc0->m_pCallSocket = pep0->m_pSocket;
	//	//		}

	//	//		return;
	//	//	}
	//	//}
	//}
				







	// 重置优先级和最大最小层数等
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
		m_arrColls[i]->init();

	// 计算取货数量，对下一级的影响
	for (size_t i = 0; i < nColls; i++)
		m_arrColls[i]->init2();

	// 更新优先级，不能与上一句合并，因为可能改变其它的优先级
	// =================================================================
	// 序号	区号	说明
	//	0	R201	在线充电
	//	1	R301	离线充电
	//	2	A101	下砖区
	//	3	1202	存储区
	//	4	A301	两个电梯
	//	5	1601	电梯前等待
	for (size_t i = 0; i < nColls; i++)
		m_arrColls[i]->updatePriority(pCar);

	// 新建优先级表
	CArray<Collection*> colls;
	colls.SetSize(0, nColls);
	// 优先级排降序
	for (size_t i = 0; i < nColls; i++)
	{
		for (size_t j = 0; j <= i; j++)
		{
			if (j == i)
				colls.Add(m_arrColls[i]);	// 添加一项
			else if (m_arrColls[i]->m_priority.p1 > colls[j]->m_priority.p1)
			{
				colls.InsertAt(j, m_arrColls[i]);	// 插到中间
				break;
			}
		}
	}

	// 删除较低优先级
	INT32 p = colls[0]->m_priority.p1;
	if (p <= 0)		// 没有可用点，直接返回
		return 0;

	// 20210722 坚美雅洁专用，注销这一段，不删除级别较低的，因为立库出货时，可能去充电而不是取货
	// 2022 南基项目使用，否则有呼叫也去充电，保留优先级最高的几组
	for (size_t i = 1; i < nColls; i++)
	{
		ASSERT(colls[i]->m_priority.p1 <= p);

		if (colls[i]->m_priority.p1 < p)
			colls[i] = 0;
	}

	// 20201215 注释这一段，因为充电的AGV可能是最近的，	// ??
	// 找最近的AGV
	EndPoint* pep0 = 0;		// 选中的端点
	Car* pc0 = 0;			// 选中的AGV
	INT32 dis0 = MAXINT32;	// 最短距离
	for (size_t i = 0, length = theApp.m_pGround->m_arrCars.GetCount(); i < length; i++)
	{
		Car* pc = theApp.m_pGround->m_arrCars[i];
		if (pc->getOutside())	// 不能用getDisable
			continue;			// 停用，跳过

		//====11 潜伏式，取货也可以再去取货
		//if (pc->m_pReadData->nState == 0x1045)
		//	continue;	// 取货时不再去取货

		//ASSERT(pc->m_pEndPoint);
		//if (!pc->m_pEndPoint)
		//	continue;

		// 20220806 初次使用可能没有初始化
		if (!pc->m_arrPath.GetCount())
			continue;

		if (!pc->waiting()/* && !pc->isInit()*/
			&& pc->m_pEndPoint
			&& !(pc->m_pEndPoint->isRecharge()		// 20220411 非充电，充电过程可重新规划路线
				&& theApp.m_bBreakRecharge
				)
			)
		{
			continue;	// 不是等待和初始化，跳过
		}

		// 如果正在充电，并且电量低，跳过
		if (
			pc->m_pEndPoint
			&& pc->m_pEndPoint->isRecharge()
			&& pc->isLowBattery()
			)
		{
			continue;
		}

		// 20210715 坚美雅洁 在A区等待也参与计算
		//if (pc->m_pEndPoint && pc->m_pEndPoint->m_bufCode[0] == L'A')
		//	continue;	// 在Axxx区等待，不参与计算

		if (pc->m_bLoaded)
			continue;	// 拉空托在充电 ????

		for (size_t j = 0; j < nColls && colls[j]; j++)
		{
			if (((EndPoint::PointType&)colls[j]->m_pointType).m_bStart)	// 发车点集合
				//if (colls[j]->m_pointType == POINT_TYPE::POINT_START)
				continue;					// 排除发车点

			INT32 dis = 0;
			EndPoint* pep = colls[j]->getPickupPoint(pc, dis);
			if (!pep)
				continue;



			// 20210716 坚美雅洁专用 充电点优先到1号点（A302），没有解决前往充电又呼叫取货的问题，F:\指挥调度系统\GHCY潜伏2T\调度和避让问题.docx\20210716 0603
			//if((pep && *(UINT64*)pep->getCode() == *(UINT64*)L"A302")			// 到1号点，A302
			//	)
			//{
			//	if ((充电AGV == pc)
			//		//&& ((pc->m_pReadData->nCardType1 & 0x00ff) == 0x0011)	// 20201102 在线充电时直接请求运行，推行或拉
			//		//&& (pc->m_pReadData->nDistance >= 1000)				// ？？？？？？？？？？？？？？？？？？？ ?? 20210713 过卡后后下一条，否则不正常。有两处
			//		)
			//	{
			//		dis = 0;			// 正在充电的最优先
			//	}
			//	else if (充电AGV)
			//		continue;			// 如果有准备充电的AGV, 不到1号点
			//}




			if (dis < dis0)
			{
				pc0 = pc;
				dis0 = dis;
				pep0 = pep;
			}
		}
	}

	// 开始运行
	if (!pep0)
		return 0;

	// 如果是充电的过程中，重新规划
	if (!pc0->waiting()
		&& pc0->m_pEndPoint
		&& pc0->m_pEndPoint->isRecharge()
		)
	{
		if (theApp.m_bBreakRecharge)	// 允许充电过程中重新规划路径
		{
			// 不成功
			// 20220416 暂时关闭，因为没有解决同一个点走两次的问题
			int nGet = pep0->getRoadNode()->m_nID - 1;		// 如果是同类，直接导到终点
			if (theApp.m_pGround->carStartGet2(pc0, nGet))
			{
				// 设置AGV对应的呼叫点通讯实例
				//pc0->m_pCallSocket = pep0->m_pSocket;
				return 1;
			}
		}
	}
	// 否则，从起点规划
	else
	{
		int nGet = pep0->getRoadNode()->m_nID - 1;	// 如果是同类，直接导到终点
		if (theApp.m_pGround->carStartGet(pc0, nGet))
		{
			// 设置AGV对应的呼叫点通讯实例
			//pc0->m_pCallSocket = pep0->m_pSocket;
			return 1;
		}
	}

	return 0;
}

/// <类型>Fanction</类型>
/// <作者>Mr. Hu</作者>
/// <日期>20190901</日期>
/// <功能>能否到达指定的点</功能>
/// <参数 name = "nSerial">发车点号</参数>
/// <参数 name = "nPoint">端点号，id-1</参数>
/// <参数 name = "bFirst">指定终眯集合</参数>
/// <异常 name = "GException">没有找到发车点<异常>
/// <返回>能到TRUE，不能到FALSE</返回>
/// <说明>指定的发车点能否到达指定的端点，如果bFirst==TRUE并且私有终点表有值，用私有终点表判断，否则用公用终点表</说明>
BOOL Collections::isStartFriend(int nSerial, int nPoint, BOOL bFirst)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		if (((EndPoint::PointType&)pColl->m_pointType).m_bStart)	// 发车点集合
		//if (pColl->m_pointType == POINT_TYPE::POINT_START)	// 发车点集合
		{
			if (((CollStarts*)pColl)->isFriend(nSerial, nPoint, bFirst))
			{
				return TRUE;
			}
		}
	}

	// 没有对应的发车点
	// 保存日志
	GException* pge = new GException;
	pge->m_nCode = 2041;
	pge->m_strSummary = L"发车点错误2";
	pge->m_strDetails.Format(L"AGV发车点 %i 错误。", nSerial);

	THROW(pge);

	//return FALSE;

	// ?? 改变私有点和公有点用法
	// 发车点号，如果bFirst==TRUE并且私有终点表有值，用私有终点表判断，否则用公用终点表
	//CArray<int>& arrPoints = bFirst && m_arrStartPoints[i]->m_arrPoints1.GetCount()
	//	? m_arrStartPoints[i]->m_arrPoints1 : m_arrStartPoints[i]->m_arrPoints2;
	//length = arrPoints.GetCount();
	//i = 0;
	//for (; i < length && arrPoints[i] != nPoint; i++);

	//return i < length;
}

/// <类型>Fanction</类型>
/// <作者>Mr. Hu</作者>
/// <日期>20190901</日期>
/// <功能>取发车点对应的节点</功能>
/// <参数 name = "nSerial">发车点号</参数>
/// <异常 name = "GException">没有找到发车点<异常>
/// <返回>对应的道路节点，id-1</返回>
/// <说明>根据发车点号查找对应的道路节点(id-1)，如果没有发车点抛出异常</说明>
int Collections::getStartNode2(int nSerial)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		if (((EndPoint::PointType&)pColl->m_pointType).m_bStart)	// 发车点集合
		//if (pColl->m_pointType == POINT_TYPE::POINT_START)	// 发车点集合
		{
			int id = ((CollStarts*)pColl)->getNode2(nSerial);
			if(id)
			{
				return id;
			}
		}
	}

	// 没有找到发车点
	// 保存日志
	GException* pge = new GException;
	pge->m_nCode = 2041;
	pge->m_strSummary = L"发车点错误1";
	pge->m_strDetails.Format(L"AGV发车点 %i 错误。", nSerial);

	THROW(pge);

	//return  0;
}

/// <类型>Fanction</类型>
/// <作者>Mr. Hu</作者>
/// <日期>20190901</日期>
/// <功能>取发车点</功能>
/// <参数 name = "nSerial">发车点号</参数>
/// <返回>对应的发车点指针</返回>
/// <说明>如果没有发车点抛出异常</说明>
EndPoint* Collections::getStartPoint(int nSerial)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		// 类型不匹配，不是发车点
		if (!((EndPoint::PointType&)pColl->m_pointType).m_bStart)	// 发车点集合
		//if (pColl->m_pointType != POINT_TYPE::POINT_START)
		{
			continue;
		}

		// 取发车点
		for (size_t j = 0, length = pColl->m_arrPoint.GetSize(); j < length; j++)
		{
			if (pColl->m_arrPoint[j]->m_nSerial == nSerial)
			{
				return pColl->m_arrPoint[j];
			}
		}
	}

	// 没有找到发车点
	// 保存日志
	GException* pge = new GException;
	pge->m_nCode = 2041;
	pge->m_strSummary = L"发车点错误1";
	pge->m_strDetails.Format(L"AGV发车点 %i 错误。", nSerial);

	THROW(pge);

	//return  0;
}

/// <类型>Fanction</类型>
/// <作者>Mr. Hu</作者>
/// <日期>20190902</日期>
/// <功能>是不是发车点</功能>
/// <参数 name = "nNode">道路节点号id-1</参数>
/// <返回>对应的发车点号，-1没有</返回>
/// <异常 name = ""><异常>
/// <说明>判断给定的道路节点是不是发车点</说明>
int Collections::isStartNode(int nNode)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		if (((EndPoint::PointType&)pColl->m_pointType).m_bStart)	// 发车点集合
		//if (pColl->m_pointType == POINT_TYPE::POINT_START)	// 发车点集合
		{
			int nStart = ((CollStarts*)pColl)->isStartNode(nNode);
			if (nStart >= 0)
			{
				return nStart;
			}
		}
	}

	return -1;
}

/// <作者>Mr. Hu</作者>
/// <日期>20210413</日期>
/// <功能>取道路节点id对应的端点id</功能>
/// <参数 name = "nType">节点类型</参数>
/// <参数 name = "nNodeID">道路节点号id</参数>
/// <返回>取道路节点id对应的端点id，0没找到</返回>
/// <异常 name = ""><异常>
/// <说明>通过类型和道路ID查找对应的端点，不用道路节点的m_nPoint属性，因为一个节点可能对应多个端点，如充电和发车</说明>
//EndPoint* Collections::getPoint(POINT_TYPE nType, int nNodeID)
//{
//	// 遍历发车集
//	size_t nColls = m_arrColls.GetCount();
//	for (size_t i = 0; i < nColls; i++)
//	{
//		Collection* pColl = m_arrColls[i];
//		// 类型不匹配
//		if (pColl->m_pointType != nType)
//		{
//			continue;
//		}
//
//		// 取端点
//		for (size_t j = 0, length = pColl->m_arrPoint.GetSize(); j < length; j++)
//		{
//			if (pColl->m_arrPoint[j]->m_pRoadNode->m_nID == nNodeID)
//			{
//				return pColl->m_arrPoint[j];
//			}
//		}
//	}
//
//	return 0;
//}

/// <作者>Mr. Hu</作者>
/// <日期>20240923</日期>
/// <功能>获取站点编码对应的集合</功能>
/// <参数 name = "bufCode">unicode 字符串，要求4个字，8个字节</参数>
/// <返回>找到的集合，0没找到</返回>
/// <异常 name = ""><异常>
/// <说明></说明>
Collection* Collections::getColl(LPCTSTR bufCode)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		// 类型不匹配
		if (*(UINT64*)bufCode == *(UINT64*)pColl->m_bufCode)		// 遍历相同的编号
		{
			return pColl;
		}
	}

	return 0;
}

/// <作者>Mr. Hu</作者>
/// <日期>202401007</日期>
/// <功能>执行任务</功能>
/// <参数 name = "m_pRecBuf">接收数据缓冲</参数>
/// <返回></返回>
/// <说明> 线程中运行 </说明>
void Collections::wcsTask(PBYTE m_pRecBuf)
{
	WcsTask& taskData = (WcsTask&)*m_pRecBuf;

	// 把站点代码转换成unicode字符串
	CString strCode((PCHAR)taskData.nDest);
	CString strNumber((PCHAR)taskData.sNumber);				// 把sNumber转换成unicode字符串

	// 查找空闲AGV
	EndPoint* pep = 0;						// 选中的端点
	Car* pCar = 0;
	WcsSatae wcsSatae;
	if (taskData.bGet && taskData.bPut)
	{
		GException ge;
		ge.m_nCode = 2033;
		ge.m_strObject = L"WCS任务";
		ge.m_strSummary = L"存货取货同时有效", taskData.nCar;
		//ge.m_strDetails.Format(L"SID:%i，sNumber:%s", taskData.nSid, (LPCTSTR)sNumber);
		theApp.m_pLogList->addLog(&ge);

		wcsSatae.bErrGetPut = 1;
	}
	else if (taskData.nCar)										// 任务中指定AGV，直接用
	{
		if (taskData.nCar > 255								// AGV编号超范围
			|| !theApp.m_pGround->m_carList[taskData.nCar]
			)
		{
			GException ge;
			ge.m_nCode = 2032;
			ge.m_strObject = L"WCS任务";
			ge.m_strSummary.Format(L"AGV编号(%i)无效", taskData.nCar);
			ge.m_strDetails.Format(L"SID:%i，sNumber:%s", taskData.nSid, (LPCTSTR)strNumber);
			theApp.m_pLogList->addLog(&ge);

			wcsSatae.bErrAgvNumber = 1;
		}
		else													// 取指定的AGV
		{
			pCar = theApp.m_pGround->m_carList[taskData.nCar];
			pep = matchPoint(pCar, taskData, (PTCHAR)(LPCTSTR)strCode);
			if (!pep)
			{
				pCar = 0;
				wcsSatae.bErrDest = 1;
			}
		}
	}
	else									// 未指定AGV，查找最近的AGV
	{
		int dis0 = MAXINT32;	// 最短距离
		for (size_t i = 0, length = theApp.m_pGround->m_arrCars.GetCount(); i < length; i++)
		{
			Car* pc = theApp.m_pGround->m_arrCars[i];
			if (!pc->waiting())								// 不是等待取货
				continue;									// 停用，跳过

			//if (taskData.bGet && pc->m_bLoaded				// 存取货不匹配，跳过
			//	|| taskData.bPut && !pc->m_bLoaded
			//	)
			//	continue;

			int dis = 0;
			EndPoint* pep2 = matchPoint(pc, taskData, (PTCHAR)(LPCTSTR)strCode, &dis);
			if (!pep2)
				continue;
			if(!pc->initCar(0))
				continue;

			// 清除路径
			//pc->removePath(0, pc->m_arrPath.GetCount());

			// 求最近的AGV
			if (dis < dis0)
			{
				pCar = pc;
				dis0 = dis;
				pep = pep2;
			}
		}
	}

	// 如果是充电的过程中，重新规划
	/*if (!pc0->waiting()
		&& pc0->m_pEndPoint
		&& pc0->m_pEndPoint->isRecharge()
		)
	{
		if (theApp.m_bBreakRecharge)	// 允许充电过程中重新规划路径
		{
			// 不成功
			// 20220416 暂时关闭，因为没有解决同一个点走两次的问题
			int nGet = pep0->getRoadNode()->m_nID - 1;		// 如果是同类，直接导到终点
			if (theApp.m_pGround->carStartGet2(pc0, nGet))
			{
				// 设置AGV对应的呼叫点通讯实例
				//pc0->m_pCallSocket = pep0->m_pSocket;
				return 1;
			}
		}
	}
	*/

	if (pCar && !pCar->m_arrPath.GetCount())		// 没有发车时
		if (!pCar->initCar())						// 找发车点
		{
			pCar = 0;								// 停用，跳过
		}

	if(!pCar)
	{
		GException ge;
		ge.m_nCode = 2035;
		ge.m_strObject = L"WCS任务";
		ge.m_strSummary = L"没有匹配的任务点，或AGV不在线";
		//ge.m_strDetails.Format(L"SID:%i，sNumber:%s", taskData.nSid, (LPCTSTR)sNumber);
		theApp.m_pLogList->addLog(&ge);

		wcsSatae.bNoAGV = 1;
	}
	else if (!pep)
	{
		GException ge;
		ge.m_nCode = 2031;
		ge.m_strObject = L"WCS任务";
		ge.m_strSummary = L"没有找到指定终点";
		ge.m_strDetails.Format(L"站点代码:%s，序号%i", (LPCTSTR)strCode, taskData.nSerial);
		theApp.m_pLogList->addLog(&ge);

		wcsSatae.bErrDest = 1;
	}
	else	// 找到AGV开始运行
	{
		int nGet = pep->getRoadNode()->m_nID - 1;				// 如果是同类，直接导到终点
#pragma 警告：考虑各种情况，充电，电梯等
		BOOL bb = taskData.bGet
			? theApp.m_pGround->carStartGet(pCar, nGet)	// 计算路径并运行
			: theApp.m_pGround->carStartGet(pCar, nGet);	// 计算路径并运行
		if (!bb)
		{
			wcsSatae.bNoAGV = 1;
		}
	}

	// 响应任务指令
	INT_PTR nPathCount = wcsSatae.nState ? 0							// 如果异常，路径长度0
		: pCar->m_arrPath.GetCount();
	
	if(pCar)
		pCar->m_bWaiting = !nPathCount;

	INT_PTR len = sizeof(WcsPath) + nPathCount * sizeof(WcsLine);		// 数据总长度，包括头和路径
	HBuffer buf((int)len);												// 申请内存，自动释放
	WcsPath& wcsPath = (WcsPath&)*buf.getBuf();						// 转换成 WcsPath 结构
	wcsPath.nCode = 1;												// 路径数据
	wcsPath.nSid = taskData.nSid;									// 返回sid
	wcsPath.nCar = !taskData.nCar ? !pCar ? 0
		: pCar->m_nFinsNode : taskData.nCar;						// 如果没有指定agv，并且没有空闲agv，取0，否则返回agv编号，如果指定了AGV，无论是否可用，返回指定的AGV编号
	memcpy(wcsPath.sNumber, taskData.sNumber,
		sizeof(wcsPath.sNumber));									// 任务编号
	wcsPath.nState = wcsSatae.nState;								// 任务状态，0正常，详见声明
	wcsPath.nLength = (UINT32)nPathCount;							// 路径节点数量
	// 路径
	if (nPathCount > 0)
	{
		ASSERT(nPathCount > 1);
		WcsLine* pLine = (WcsLine*)(buf.getBuf() + sizeof(WcsPath));				// 路径节点表起点
		PathNode* ppnPrevious = &pCar->m_arrPath[0];								// 前一点
		PathNode* ppnCurrent = ppnPrevious;											// 当前点
		for (int i = 0; i < nPathCount; i++, pLine++)								// 传送路径节点，详见声明
		{
			// 求道路方向，上行，下行
			ppnPrevious = ppnCurrent;
			ppnCurrent = &pCar->m_arrPath[i];										// 当前点，节点号就是行驶路段
			bool bDown = 0;
			if (ppnCurrent->m_nID > 1)												// 排除道路表第一点
			{
				int nCard0 = afxpArrRoads->GetAt(ppnCurrent->m_nID - 2).m_nCard;		// 道路上的前一点
				if (nCard0 == ppnPrevious->m_nCard)									// 前一点道路卡号和路径卡号相同，说明是下行
					bDown = 1;
			}
			// 求AGV方向，前进后退
			bool bBackward = (ppnPrevious->m_nAgvAngle != 0);						// 前进推行1
			// 取AGV速度
			int nSpeedSerial = ((int)bBackward << 1) | (int)bDown;
			pLine->fSpeed = (float)ppnCurrent->m_bufSpeed[nSpeedSerial];			// 取运行速度
			if (!pLine->fSpeed)
				pLine->fSpeed = bBackward ? (float)theApp.m_nRunSpeed : (float)theApp.m_nSlowSpeed;

			// 把AGV坐标变为地图坐标
			ASSERT(pCar->m_carData.nFloor == (INT32)ppnCurrent->m_nFloor);
			INT8 nn = (UINT32)ppnCurrent->m_nFloor >= MAXFLOOR
				? -1 : afxFloors.m_bufFloors[ppnCurrent->m_nFloor];
			D2D1_POINT_2F pt = { (float)ppnCurrent->m_nX, (float)ppnCurrent->m_nY };
			if (-1 != nn)
			{
				Floor& floor = afxFloors.m_arrFloors[nn];
				D2D1::Matrix3x2F& mtx = floor.mtxMap2Agv;
				pt = mtx.TransformPoint(pt);		// 转换到终点
			}

			// 求点类型
			int nPointID = findEndPoint(ppnCurrent->m_nID - 1);
			if (nPointID > 0)
			{
				EndPoint* pep2 = theApp.m_pGround->m_arrEndPoint[(INT_PTR)nPointID - 1];
				pLine->nType = pep2->m_nType;
			}
			else
			{
				pLine->nType = 0;
			}

			pLine->nSerial = i;
			//pLine->fSpeed = theApp.m_nRunSpeed;
			pLine->fRad = ppnCurrent->m_nAgvAngle * M_DEG1;
			pLine->nCard = ppnCurrent->m_nCard;
			pLine->nX = (INT32)pt.x;//+ ffdx
			pLine->nY = (INT32)pt.y;
			pLine->nTurnDis = ppnCurrent->m_nTurnDis;
			pLine->nProtection = 0x1111;
			if(pLine->bRecharge)
				pLine->nProtection = 0x111F;
		}

		// 最后一点
		WcsLine* pl2 = --pLine;						// 退到最后一点
		WcsLine* pl1 = --pLine;						// 倒数第二点
		pl2->nType = taskData.nType;				// 调取任务
		//if (taskData.bLift || taskData.bRecharge)
		//{
		//	//pl1->fSpeed = theApp.m_nSlowSpeed;
		//	pl2->fSpeed = theApp.m_nSlowSpeed;
		//}
		int pid = ppnCurrent->m_nPointID;
		if (ppnCurrent->m_nPointID)						// 根据垛数计算存取货坐标
		{
			EndPoint* pep2 = theApp.m_pGround->m_arrEndPoint[(INT_PTR)pid - 1];
			int nn = pep2->m_nXCurrent;

#pragma ！！！暂时把取货nn设置为0，台州用，到任意点取货，以后加站点属性
			if (taskData.bGet)
				nn = 0;

			if(nn && (taskData.bGet						// 取货，或者存货码上层，减一垛
				|| (taskData.bPut && (pep2->m_nZCurrent < pep2->m_nZNumber))
				)
			)
			{
				nn--;
			}
			int ll = nn * pep2->m_nDX;
			float dx = float(pl1->nX - pl2->nX);
			float dy = float(pl1->nY - pl2->nY);
			float len2 = sqrt(dx * dx + dy * dy);
			float k = ll / len2;
			pl2->nX += int(dx * k);
			pl2->nY += int(dy * k);

			PathNode* ppn0 = &pCar->m_arrPath[nPathCount - 2];	// 路径倒数第二点
			dx = float(ppn0->m_nX - ppnCurrent->m_nX);
			dy = float(ppn0->m_nY - ppnCurrent->m_nY);
			ppnCurrent->m_nX += int(dx * k);
			ppnCurrent->m_nY += int(dy * k);
		}
	}
	
	// 初始化AGV的载货状态
	if (pCar)
	{
		if (taskData.bGet)
			pCar->m_bLoaded = 0;
		else if (taskData.bPut)
			pCar->m_bLoaded = 1;

		// 把任务号复制心跳中
		memcpy(pCar->m_returnWcs.sNumber, taskData.sNumber, sizeof(taskData.sNumber));
	}
	theApp.m_pGround->m_socketWcs.sendData((LPCSTR)buf.getBuf(), (int)len);			// 返回任务指令

	// ?? 此处需要等待AGV从心跳中回复运行状态
}

/// <作者>Mr. Hu</作者>
/// <日期>202401008</日期>
/// <功能>根据站号和序号查找站点</功能>
/// <参数 name = "wcsTask">任务数据</参数>
/// <参数 name = "sCode">站点代码，四位unicode字符</参数>
/// <返回>站点指针，0 未找到</返回>
/// <说明> 线程中运行 </说明>
//EndPoint* Collections::findPoint(WcsTask& wcsTask, PTCHAR sCode)
//{
//	EndPoint* pep = 0;
//	for (size_t j = 0, nColls = m_arrColls.GetCount(); j < nColls; j++)
//	{
//		Collection* pColl = m_arrColls[j];
//		if (*(UINT64*)sCode != *(UINT64*)pColl->m_bufCode)		// 遍历相同的编号
//			continue;
//
//		if (wcsTask.nSerial >= pColl->m_arrPoint.GetCount())			// 数据库中的serial从0开始，并且连续
//			continue;
//
//		EndPoint* pep2 = pColl->m_arrPoint[wcsTask.nSerial];
//		if (wcsTask.bGet && !pep2->allowPickup(0))
//			continue;
//		if (wcsTask.bPut && !pep2->allowStore(0))
//			continue;
//		if(theApp.m_pGround->pointBeUsed(pep2))
//			continue;
//
//		pep = pep2;
//		break;
//	}
//	return pep;
//}

/// <作者>Mr. Hu</作者>
/// <日期>202401008</日期>
/// <功能>查找第一个可用站点</功能>
/// <参数 name = "wcsTask">任务数据</参数>
/// <返回>站点指针，0 未找到</返回>
/// <说明> 线程中运行 </说明>
//EndPoint* Collections::findPoint(WcsTask& wcsTask)
//{
//	EndPoint* pep = 0;
//	for (size_t j = 0, nColls = m_arrColls.GetCount();
//		j < nColls && !pep; j++)
//	{
//		// 匹配类型相同的集合
//		Collection* pColl = m_arrColls[j];											// 站点集
//		EndPoint::PointType& pt = ((EndPoint::PointType&)pColl->m_pointType);		// 集合类型
//		if((wcsTask.bGet && !pt.m_bGet) || (wcsTask.bPut && !pt.m_bPut))			// 存取货不匹配，路过
//			continue;
//
//		// 如果AGV指定专用线路，匹配专用的线路
//		//Collection* pColl2 = pColl->m_pPreColl;
//		//TCHAR cc = pColl->m_pPreColl
//		//if()
//
//		for (INT_PTR i = pColl->m_arrPoint.GetCount() - 1; i >= 0; i--)
//		{
//			EndPoint* pep2 = pColl->m_arrPoint[i];
//			if (wcsTask.bGet && !pep2->allowPickup(0))
//				continue;
//			if (wcsTask.bPut && !pep2->allowStore(0))
//				continue;
//			if (theApp.m_pGround->pointBeUsed(pep2))
//				continue;
//
//			pep = pep2;
//
//			if(pep->m_bActived)
//				break;
//		}
//	}
//	return pep;
//}

/// <作者>Mr. Hu</作者>
/// <日期>202401025</日期>
/// <功能>已知任务和AGV，找匹配点</功能>
/// <参数 name = "pCar">指定AGV</参数>
/// <参数 name = "wcsTask">任务数据</参数>
/// <参数 name = "sCode">站点代码</参数>
/// <参数 name = "pdis">返回匹配点到AGV的距离</参数>
/// <返回>站点站点，0 未找到</返回>
/// <说明>没有找到匹配点时，距离保持原值</说明>
EndPoint* Collections::matchPoint(Car* pCar, WcsTask& taskData, PTCHAR sCode, int* pdis)
{
	// 指定站点
	EndPoint* pep = 0;
	if (*sCode)
	{
		for (size_t j = 0, nColls = m_arrColls.GetCount(); j < nColls; j++)
		{
			Collection* pColl = m_arrColls[j];
			if (*(UINT64*)sCode != *(UINT64*)pColl->m_bufCode)				// 遍历相同的编号
				continue;

			if (taskData.nSerial >= pColl->m_arrPoint.GetCount())			// 数据库中的serial从0开始，并且连续
				continue;

			// AGV匹配站点
			LPCTSTR strLine = pCar->m_strLine;								// AGV工作线号集合
			bool r = pCar->m_strLine.IsEmpty();
			if (!r)
			{
				PTCHAR pCode = sCode;
				if (taskData.bPut)											// 如果是存货取上一级线号
				{
					if (!pColl->m_pPreColl)
						continue;

					pCode = pColl->m_pPreColl->m_bufCode;
				}

				for (size_t i = 0; !r && strLine[i]; i++)
				{
					r = strLine[i] == pCode[0];
				}

				if(!r)
					continue;
			}

			EndPoint* pep2 = pColl->m_arrPoint[taskData.nSerial];

			if (taskData.bGet && !pep2->allowPickup(pCar))
				continue;
			if (taskData.bPut && !pep2->allowStore(pCar))
				continue;
			//if (theApp.m_pGround->pointBeUsed(pep2))
			//	continue;

			if(pdis)
				*pdis = pCar->calDistance(pep2->getRoadNode());

			pep = pep2;
			break;
		}
		return pep;
	}

	// 不指定站点
	ASSERT(!pep);
	for (size_t j = 0, nColls = m_arrColls.GetCount();
		j < nColls && !pep; j++)
	{
		// 匹配类型相同的集合
		Collection* pColl = m_arrColls[j];											// 站点集
		EndPoint::PointType& pt = ((EndPoint::PointType&)pColl->m_pointType);		// 集合类型
		if ((taskData.bGet && !pt.m_bGet) || (taskData.bPut && !pt.m_bPut))			// 存取货不匹配，路过
			continue;

		// 如果AGV指定专用线路，匹配专用的线路
		TCHAR cc[2] = { 0, 0 };
		if (!pCar->m_strLine.IsEmpty())						// 指定站点
		{
			if (taskData.bGet)								// 如果取货，取站点第一个字符
				cc[0] = pColl->m_bufCode[0];
			else if (taskData.bPut)							// 存货，取父站点第一个字符
			{
				Collection* pColl2 = pColl->m_pPreColl;
				if (pColl2)
					cc[0] = pColl2->m_bufCode[0];
			}
		}
		if (!cc[0] || pCar->m_strLine.FindOneOf(cc) < 0)
			continue;										// 站号不匹配，跳过

		// 搜索子集
		for (INT_PTR i = pColl->m_arrPoint.GetCount() - 1; i >= 0; i--)
		{
			EndPoint* pep2 = pColl->m_arrPoint[i];
			if (taskData.bGet && !pep2->allowPickup(0))
				continue;
			if (taskData.bPut && !pep2->allowStore(0))
				continue;
			if (theApp.m_pGround->pointBeUsed(pep2))
				continue;

			pep = pep2;

			if (pep->m_bActived)
				break;
		}
	}

	if(pep && pdis)
		*pdis = pCar->calDistance(pep->getRoadNode());

	return pep;
}

/// <作者>Mr. Hu</作者>
/// <日期>20241124</日期>
/// <功能>求卡号对应的站点</功能>
/// <参数 name = "nNode">从0开始的道路节点序号</参数>
/// <返回>从1开始的站点ID，0 未找到</返回>
/// <说明></说明>
int Collections::findEndPoint(int nNode)
{
	// 查找起始站点，同一节点可能对应多个卡号，取卡号对应的节点集合
	RoadNode* prn = &theApp.m_pGround->m_arrRoadNodes[nNode];
	int nPointID = prn->m_nPointID;
	if (nPointID)
		return nPointID;
	// 如果节点不是站点，查找卡号对应的站点
	int nCard = prn->m_nCard;
	if (nCard <= 0)					// 负数表求终点，返回-1
		return -1;
	NodeColl* pnc1 = m_nodesColl.m_pArrCards[nCard];
	ASSERT(pnc1);
	if (!pnc1)
		return -1;										// 没有对应的集合，返回-1

	// 查找站点
	for (size_t i = 0, length = pnc1->m_pArrRoad.GetCount(); i < length; i++)
	{
		prn = pnc1->m_pArrRoad[i];
		if (prn->m_nPointID)
		{
			nPointID = prn->m_nPointID;
			nCard = prn->m_nCard;
			break;
		}
	}
	return nPointID;
}


